/*
 * Copyright (c) 2009. Silenus Consultoria, S.L.
 */
package es.silenus.detecta.controller;

import es.silenus.detecta.model.ReportFormat;
import es.silenus.detecta.model.User;

import es.silenus.detecta.util.JasperReportDefinition;
import es.silenus.detecta.util.JasperReportsHelper;

import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.fill.JRFileVirtualizer;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

import org.apache.commons.lang.RandomStringUtils;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.context.support.MessageSourceResourceBundle;

import org.springframework.core.io.Resource;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import java.sql.Connection;
import java.sql.SQLException;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import javax.sql.DataSource;


/**
 * Report controller.
 *
 * @author <a href="mailto:malonso@silenus-consultoria.es">Mariano Alonso</a>
 *
 * @since 21-may-2009 13:39:01
 */
public class ReportController extends BaseController {
	/**
	 * Logger for this class.
	 */
	private static final Log LOG = LogFactory.getLog(ReportController.class);

	/**
	 * The data source.
	 */
	private DataSource dataSource;

	/**
	 * The jasper reports helper.
	 */
	private JasperReportsHelper jasperReportsHelper;

	/**
	 * The temp report path.
	 */
	private String tempReportPath;

	/**
	 * The reports definitions.
	 */
	private Map<String, JasperReportDefinition> reportDefinitions;

	/**
	 * Sets the data source.
	 *
	 * @param dataSource the data source.
	 */
	public void setDataSource(final DataSource dataSource) {
		this.dataSource = dataSource;
	}

	/**
	 * Sets the jasper reports helper.
	 *
	 * @param jasperReportsHelper the jasper reports helper.
	 */
	public void setJasperReportsHelper(final JasperReportsHelper jasperReportsHelper) {
		this.jasperReportsHelper = jasperReportsHelper;
	}

	/**
	 * Sets the temp report path from a resource.
	 *
	 * @param resource the resource.
	 */
	public void setTempReportPath(final Resource resource) {
		if(resource != null) {
			try {
				this.tempReportPath = resource.getFile().getAbsolutePath();
			} catch(IOException e) {
				LOG.error("Error loading temp report path", e);
			}
		} else {
			this.tempReportPath = null;
		}
	}

	/**
	 * Setss the reports.
	 *
	 * @param reportDefinitions the reports.
	 */
	public void setReportDefinitions(Map<String, JasperReportDefinition> reportDefinitions) {
		this.reportDefinitions = reportDefinitions;
	}

	/**
	 * Generates a report. REQUEST PARAMETERS: - report - virtualize - format - disposition
	 *
	 * @param request the request.
	 * @param response the response.
	 *
	 * @return the view name or null.
	 */
	public String generateReport(final HttpServletRequest request, final HttpServletResponse response) {
		final User current = getUser(request);

		final String reportName = request.getParameter("report");
		final JasperReportDefinition reportDefinition = reportDefinitions.get(reportName);


		final boolean virtualize = request.getParameter("virtualize") != null;
		String format = request.getParameter("format");

		if(format == null) {
			format = reportDefinition.getFormat();
		}

		final ReportFormat theFormat = ReportFormat.valueOf(format);

		String disposition = request.getParameter("disposition");

		if(disposition == null) {
			disposition = reportDefinition.getDisposition();
		}

		String fileName = reportDefinition.getFileName(current.getLocale()) + theFormat.getExtension();


		Connection connection = null;
		FileInputStream fis = null;
		File theFile = null;
		File virtualDirectory = null;

		try {
			connection = dataSource.getConnection();

			// Create virtualization directory or temporary directory
			String dirName = RandomStringUtils.randomAlphanumeric(10);

			virtualDirectory = new File(tempReportPath, dirName);
			FileUtils.forceMkdir(virtualDirectory);

			Map<String, Object> parameters = new HashMap<String, Object>();

			// Prepare virtualization
			if(virtualize) {
				JRFileVirtualizer virtualizer = new JRFileVirtualizer(10, virtualDirectory.getAbsolutePath());

				parameters.put(JRParameter.REPORT_VIRTUALIZER, virtualizer);
			}


			// Add request parameters.
			Map<String, String[]> requestParameters = request.getParameterMap();
			String[] value;

			for(Map.Entry<String, String[]> entry : requestParameters.entrySet()) {
				value = entry.getValue();

				if(value.length == 1) {
					parameters.put(entry.getKey(), value[0]);
				} else {
					parameters.put(entry.getKey(), value);
				}
			}

			// Add organization
			parameters.put("organization", current.getOrganization().getId());

			MessageSourceResourceBundle bundle = new MessageSourceResourceBundle(this.messageSource, current.getLocale());

			parameters.put(JRParameter.REPORT_LOCALE, current.getLocale());
			parameters.put(JRParameter.REPORT_RESOURCE_BUNDLE, bundle);
			parameters.put("SUBREPORT_DIR", reportDefinition.getReportDirectoryPath());

			// parameters.put(IMAGE_BASE_URL, imageBaseDirectory.getAbsolutePath() + File.separator);

			// Fill the report
			JasperPrint printedReport = JasperFillManager.fillReport(
					reportDefinition.getReportFilePath(), parameters, connection);

			// Create temp file
			theFile = new File(virtualDirectory, fileName);

			JRExporter exporter = jasperReportsHelper.getExporter(format);

			exporter.setParameter(JRExporterParameter.JASPER_PRINT, printedReport);
			exporter.setParameter(JRExporterParameter.OUTPUT_FILE, theFile);
			exporter.setParameter(JRExporterParameter.FILE_RESOLVER, jasperReportsHelper.getFileResolver());

			exporter.exportReport();

			response.setContentType(getServletContext().getMimeType(theFile.getName()));
			response.setHeader("Content-Disposition", disposition + "filename=\"" + theFile.getName() + "\"");
			response.setContentLength((int)theFile.length());

			fis = new FileInputStream(theFile);
			IOUtils.copy(fis, response.getOutputStream());
		} catch(Throwable e) {
			LOG.error("Error generating report", e);
		} finally {
			// Close connection
			if(connection != null) {
				try {
					connection.close();
				} catch(SQLException e) {
					// hide exception
				}
			}

			// Close input stream
			IOUtils.closeQuietly(fis);

			// Clean virtual directory
			try {
				FileUtils.cleanDirectory(virtualDirectory);
			} catch(IOException e) {
				LOG.error("Error cleaning virtual directory", e);
			}
		}

		return null;
	}
}
